/*	$Id$	*/
/*
 * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

%{
#include <sys/param.h>

#include <errno.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>

#include "pkt.h"
#include "queue.h"
#include "pforge.h"


#define PFEXPR_OP_EQ      1
#define PFEXPR_OP_NEQ     2
#define PFEXPR_OP_GT      3
#define PFEXPR_OP_GTEQ    4
#define PFEXPR_OP_LT      5
#define PFEXPR_OP_LTEQ    6
#define PFEXPR_OP_NOT     7
#define PFEXPR_OP_AND     8
#define PFEXPR_OP_OR      9
#define PFEXPR_OP_CTN    10

struct pf_expr_node {
};


struct pf_expr {
	struct pf_expr_node *ex_root;
};




typedef struct {
	union {
		uint64_t  num;
		int       op;
		char      *string;
	} v;

} YYSTYPE;


/* Expression operators
 *
 * This table must be kept in the appropriate order so operators whose string
 * contains another operator get matched before.
 */

static const struct pf_expr_op {
	int         op_id;
	const char  op_str[4];
	int        (*op_hdlr)(void);
} operators[] = {
	{ PFEXPR_OP_GTEQ,   ">=",   NULL    },
	{ PFEXPR_OP_LTEQ,   "<=",   NULL    },
	{ PFEXPR_OP_EQ,     "=",    NULL    },
	{ PFEXPR_OP_NEQ,    "!=",   NULL    },
	{ PFEXPR_OP_GT,     ">",    NULL    },
	{ PFEXPR_OP_LT,     "<",    NULL    },
	{ PFEXPR_OP_NOT,    "!",    NULL    },
	{ PFEXPR_OP_AND,    "&&",   NULL    },
	{ PFEXPR_OP_OR,     "||",   NULL    },
};


static const char *yy_input;
static const char *yy_cin;	/* pointer to current index for lookup */



static int    lgetc      (FILE *);
static int    lungetc    (int, FILE *);
static int    yyerror    (const char *, ...);
static int    yylex      (void);
static int    yyparse    (void);
static int    op_lookup  (const char *);


/* parse buffer for easier macro expansion */


%}

%token  STRING OP
%type   <v.string> STRING
%type	<v.num> number
%type   <v.op>  OP
%%

stmt		: fldspec
		|
		;

expr		: /* empty */
		| expr '+' number
		| expr '-' number
		| expr '*' number
		| expr '/' number
		| number
		;

number		: STRING
		{
			char *ep;
			long res;
			res = strtol($1, &ep, 0);
			if ((res == LONG_MIN) || (res == LONG_MAX)) {
				yyerror("%sflow while converting number `%s'",
				    res == LONG_MIN ? "under" : "over", $1);
				free($1);
				YYERROR;
			}
			else if (*ep != '\0') {
				yyerror("invalid number `%s'", $1);
				free($1);
				YYERROR;
			}

			$$ = (uint64_t)res;
			free($1);
		}
		;

operator	:
		;

%%

static int
op_lookup(const char *tstr)
{
	unsigned int i;

	for (i = 0; i < (sizeof(operators)/sizeof(operators[0])); i++) {
	}

	return (OP);
}



static int
yylex(void)
{
	int ret;
	char buf[1024], c, endc, *bp, *ep;
	const char *cp;

lex_start:
	bp = buf;
	ep = buf + sizeof(buf) - 1;

	/* skip whitespace */
	while ((*yy_cin == ' ') || (*yy_cin == '\t'))
		yy_cin++;

	c = *yy_cin;
	if (c == '\0')
		ret = 0;
	else if ((c == '\'') || (c == '"')) {
		endc = c;
		while ((c = *(++yy_cin)) != endc) {
			if (c == '\0') {
				yyerror("unterminated quoted string");
				return (-1);
			}
			*bp++ = c;
			if (bp == ep) {
				yyerror("quoted string too long");
				return (-1);
			}
		}
		*bp = '\0';
		ret = STRING;
		printf("quoted string: `%s'\n", buf);
		yylval.v.string = strdup(buf);
		if (yylval.v.string == NULL) {
			return (-1);
		}
	} else {
		do {
			*bp++ = c;
			if (bp == ep) {
				yyerror("string too long");
				return (-1);
			}

			c = *(++yy_cin);
		} while ((c != EOF) && (c != ' ') && (c != '\n'));
		yy_cin--;
		*bp = '\0';
		ret = op_lookup(buf);
		if (ret != OP) {
			printf("string: `%s'\n", buf);
			yylval.v.string = strdup(buf);
			if (yylval.v.string == NULL) {
				return (-1);
			}
		}
	}

	return (ret);
}


static int
yyerror(const char *fmt, ...)
{
	va_list vap;

	va_start(vap, fmt);
	/* need to report something */
	va_end(vap);

	return (0);
}


/*
 * pf_expr_parse()
 *
 * Parse a PF packet expression string and return the corresponding expression
 * tree.
 */
PF_EXPR*
pf_expr_parse(const char *exstr)
{
	PF_EXPR *expr;

	if ((expr = (PF_EXPR *)malloc(sizeof(*expr))) == NULL) {
		pf_seterr(PF_ERR_NOMEM);
		return (NULL);
	}

	yydebug = 1;
	yy_input = exstr;

	if (yyparse() != 0) {
		pf_expr_free(expr);
		expr = NULL;
	}


	return (expr);
}

/*
 * pf_expr_free()
 *
 * Free an expression tree previously created from pf_expr_parse().
 */
void
pf_expr_free(PF_EXPR *expr)
{
	free(expr);
}
